package com.activiti.ActivitiUtils.impl;

import com.activiti.ActivitiUtils.bean.Page;
import com.activiti.ActivitiUtils.bean.WrappedProcessDefinitionBean;
import com.activiti.ActivitiUtils.bean.WrappedProcessInstanceBean;
import com.activiti.ActivitiUtils.builder.payload.process.definition.ProcessDefinitionLimitOnePayload;
import com.activiti.ActivitiUtils.builder.payload.process.definition.ProcessDefinitionQueryPayload;
import com.activiti.ActivitiUtils.builder.payload.process.instance.ProcessInstanceLimitOnePayload;
import com.activiti.ActivitiUtils.builder.payload.process.instance.ProcessInstanceQueryPayload;
import com.activiti.ActivitiUtils.builder.payload.process.instance.ProcessInstanceStartPayload;
import com.activiti.ActivitiUtils.factory.ProcessDefinitionBeanWrappedFactory;
import com.activiti.ActivitiUtils.factory.ProcessInstanceBeanWrappedFactory;
import com.activiti.ActivitiUtils.runtime.WrappedProcessRuntime;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * create by chongyahhh
 * 2020/4/4
 */

@Component
public class WrappedProcessRuntimeImpl implements WrappedProcessRuntime {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;

    private ProcessDefinitionQuery getProcessDefinitionQuery(ProcessDefinitionQueryPayload processDefinitionsPayload){
        ProcessDefinitionQuery processDefinitionQuery = this.repositoryService.createProcessDefinitionQuery();
        if(processDefinitionsPayload.getProcessDefinitionKeys() != null){
            processDefinitionQuery.processDefinitionKeys(processDefinitionsPayload.getProcessDefinitionKeys());
        }
        if(processDefinitionsPayload.getProcessDefinitionIds() != null){
            processDefinitionQuery.processDefinitionIds(processDefinitionsPayload.getProcessDefinitionIds());
        }
        return processDefinitionQuery;
    }

    private ProcessDefinition processDefinition(ProcessDefinitionLimitOnePayload processDefinitionsPayload) {
        ProcessDefinitionQuery processDefinitionQuery = this.repositoryService.createProcessDefinitionQuery();
        if(processDefinitionsPayload.getProcessDefinitionKey() != null){
            processDefinitionQuery.processDefinitionKey(processDefinitionsPayload.getProcessDefinitionKey());
        }
        if(processDefinitionsPayload.getProcessDefinitionId() != null){
            processDefinitionQuery.processDefinitionId(processDefinitionsPayload.getProcessDefinitionId());
        }
        List<ProcessDefinition> data = processDefinitionQuery.orderByProcessDefinitionVersion().asc().list();
        ProcessDefinition processDefinition;
        if (!data.isEmpty()) {
            processDefinition = data.get(0);
        } else {
            throw new RuntimeException("没有该流程定义");
        }
        return processDefinition;
    }

    private ProcessInstanceQuery getProcessInstanceQuery(ProcessInstanceQueryPayload processInstancesPayload){
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        if(processInstancesPayload.getProcessDefinitionIds() != null){
            processInstanceQuery.processDefinitionIds(processInstancesPayload.getProcessDefinitionIds());
        }
        if(processInstancesPayload.getProcessDefinitionKeys() != null){
            processInstanceQuery.processDefinitionKeys(processInstancesPayload.getProcessDefinitionKeys());
        }
        if(processInstancesPayload.isActiveOnly()){
            processInstanceQuery.active();
        }
        if(processInstancesPayload.isSuspendedOnly()){
            processInstanceQuery.suspended();
        }
        return processInstanceQuery;
    }

    private ProcessInstance processInstance(ProcessInstanceLimitOnePayload processInstancesPayload){
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        if(processInstancesPayload.getBusinessKey() != null){
            processInstanceQuery.processInstanceBusinessKey(processInstancesPayload.getBusinessKey());
        }
        if(processInstancesPayload.getId() != null){
            processInstanceQuery.processInstanceId(processInstancesPayload.getId());
        }
        List<ProcessInstance> data = processInstanceQuery.list();
        ProcessInstance processInstance;
        if(data != null && data.size() != 0){
            processInstance = data.get(0);
        } else {
            throw new RuntimeException("该流程实例不存在");
        }
        return  processInstance;
    }

    @Override
    public WrappedProcessDefinitionBean getProcessDefinition(ProcessDefinitionLimitOnePayload processDefinitionsPayload) {
        ProcessDefinition processDefinition;
        try{
            processDefinition = this.processDefinition(processDefinitionsPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return ProcessDefinitionBeanWrappedFactory.inject(processDefinition);
    }

    @Override
    public List<WrappedProcessDefinitionBean> getProcessDefinitionList(ProcessDefinitionQueryPayload processDefinitionsPayload) {
        ProcessDefinitionQuery processDefinitionQuery = this.getProcessDefinitionQuery(processDefinitionsPayload);
        List<ProcessDefinition> data = processDefinitionQuery.list();
        return ProcessDefinitionBeanWrappedFactory.inject(data);
    }

    @Override
    public Page<List<WrappedProcessDefinitionBean>> getProcessDefinitionPages(ProcessDefinitionQueryPayload processDefinitionsPayload, Integer pageNum, Integer pageSize) {
        ProcessDefinitionQuery processDefinitionQuery = this.getProcessDefinitionQuery(processDefinitionsPayload);
        Integer count = processDefinitionQuery.list().size();
        Integer totalPage = (count%pageSize==0)?(count/pageSize):(count/pageSize + 1);
        List<ProcessDefinition> data = processDefinitionQuery
                .orderByProcessDefinitionVersion().desc().listPage((pageNum - 1) * pageSize,pageSize);
        return ProcessDefinitionBeanWrappedFactory.inject(data,pageNum,pageSize,totalPage);
    }

    @Override
    public Boolean isProcessDefinitionSuspend(ProcessDefinitionLimitOnePayload processDefinitionsPayload) {
        ProcessDefinition processDefinition;
        try{
            processDefinition = this.processDefinition(processDefinitionsPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return processDefinition.isSuspended();
    }

    @Override
    public WrappedProcessDefinitionBean suspendOrActiveProcessDefinition(ProcessDefinitionLimitOnePayload processDefinitionsPayload) {
        ProcessDefinition processDefinition;
        try{
            processDefinition = this.processDefinition(processDefinitionsPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        boolean suspend = processDefinition.isSuspended();
        if(suspend){
            repositoryService.activateProcessDefinitionById(processDefinition.getId());
            System.out.println("流程定义" + processDefinition.getId() + "被激活");
        } else {
            repositoryService.suspendProcessDefinitionById(processDefinition.getId());
            System.out.println("流程定义" + processDefinition.getId() + "被挂起");
        }
        return ProcessDefinitionBeanWrappedFactory.inject(this.processDefinition(processDefinitionsPayload));
    }

    @Override
    public WrappedProcessInstanceBean getProcessInstance(ProcessInstanceLimitOnePayload processInstancesPayload) {
        ProcessInstance processInstance;
        try {
            processInstance= this.processInstance(processInstancesPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return ProcessInstanceBeanWrappedFactory.inject(processInstance);
    }

    @Override
    public List<WrappedProcessInstanceBean> getProcessInstanceList(ProcessInstanceQueryPayload processInstancesPayload) {
        ProcessInstanceQuery processInstanceQuery = this.getProcessInstanceQuery(processInstancesPayload);
        List<ProcessInstance> data = processInstanceQuery.list();
        return ProcessInstanceBeanWrappedFactory.inject(data);
    }

    @Override
    public Page<List<WrappedProcessInstanceBean>> getProcessInstancePages(ProcessInstanceQueryPayload processInstancesPayload, Integer pageNum, Integer pageSize) {
        ProcessInstanceQuery processInstanceQuery = this.getProcessInstanceQuery(processInstancesPayload);
        Integer count = processInstanceQuery.list().size();
        Integer totalPage = (count%pageSize==0)?(count/pageSize):(count/pageSize + 1);
        List<ProcessInstance> data = processInstanceQuery
                .orderByProcessInstanceId().desc().listPage((pageNum - 1) * pageSize,pageSize);
        return ProcessInstanceBeanWrappedFactory.inject(data,pageNum,pageSize,totalPage);
    }

    @Override
    public WrappedProcessInstanceBean start(ProcessInstanceStartPayload processInstancesPayload) {
        ProcessInstance processInstance = null;
        if(processInstancesPayload.getProcessDefinitionKey() != null){
            if(processInstancesPayload.getVariables() != null){
                processInstance = runtimeService.startProcessInstanceByKey(processInstancesPayload.getProcessDefinitionKey(),processInstancesPayload.getVariables());
            } else {
                processInstance = runtimeService.startProcessInstanceByKey(processInstancesPayload.getProcessDefinitionKey());
            }
        } else {
            if(processInstancesPayload.getProcessDefinitionId() != null){
                if(processInstancesPayload.getVariables() != null){
                    processInstance = runtimeService.startProcessInstanceById(processInstancesPayload.getProcessDefinitionId(),processInstancesPayload.getVariables());
                } else {
                    processInstance = runtimeService.startProcessInstanceById(processInstancesPayload.getProcessDefinitionId());
                }
            }
        }
        if(processInstance == null){
            return null;
        }
        if(processInstancesPayload.getBusinessKey() != null){
            runtimeService.updateBusinessKey(processInstance.getId(),processInstancesPayload.getBusinessKey());
        }
        return ProcessInstanceBeanWrappedFactory.inject(processInstance);
    }

    @Override
    public Boolean isProcessInstanceSuspend(ProcessInstanceLimitOnePayload processInstancesPayload) {
        ProcessInstance processInstance;
        try {
            processInstance = this.processInstance(processInstancesPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return processInstance.isSuspended();
    }

    @Override
    public WrappedProcessInstanceBean suspendOrActiveProcessInstance(ProcessInstanceLimitOnePayload processInstancesPayload) {
        ProcessInstance processInstance;
        try {
            processInstance = this.processInstance(processInstancesPayload);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        boolean suspend = processInstance.isSuspended();
        if(suspend){
            runtimeService.activateProcessInstanceById(processInstance.getId());
        } else {
            runtimeService.suspendProcessInstanceById(processInstance.getId());
        }
        return ProcessInstanceBeanWrappedFactory.inject(this.processInstance(processInstancesPayload));
    }

    @Override
    public WrappedProcessInstanceBean updateBusinessKey(String processInstanceId,String businessKey) {
        runtimeService.updateBusinessKey(processInstanceId,businessKey);
        ProcessInstance processInstance;
        try {
            processInstance = this.processInstance(new ProcessInstanceLimitOnePayload().withId(processInstanceId));
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return ProcessInstanceBeanWrappedFactory.inject(processInstance);
    }

    @Override
    public void setVariables(String executionId,Map<String,Object> variables) {
        runtimeService.setVariables(executionId,variables);
    }

    @Override
    public Map<String, Object> getVariables(String executionId) {
        return runtimeService.getVariables(executionId);
    }

    @Override
    public void removeVariables(String executionId, List<String> variableNames) {
        runtimeService.removeVariables(executionId,variableNames);
    }
}
